﻿using System;
using System.Xml;
using System.Windows;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using Microsoft.Maps.MapControl;

using DeepEarth.BingMapsToolkit.Client.Common.Entities;
using DeepEarth.BingMapsToolkit.Client.MapGeometry;
using System.Text;
using System.Xml.Linq;
using System.Linq;

using GeoAPI.Geometries;
using SMIL.DE.Extensions;

namespace SMIL.Geometry
{
    public delegate void SelectedSelectGeometryHandler(object sender, EventArgs arg);

    public class StyledGeometry
    {
        public StyleSpecification GeometryStyle { get; set; }

        public string  Style { get; set; }

        public IGeometry Geometry { get; set; }

        const double minWest = -180;
        const double maxNorth = 90;
        const double maxEast = 180;
        const double minSouth = -90;

        public static event SelectedEventHandler SelectedSelectGeometry;

        Map parentMap;

        public StyledGeometry(Map parentMap = null, StyleSpecification styleSpecification = null, IGeometry geometry = null)
        {
            if (null != parentMap)
                this.parentMap = parentMap;

            if (null != styleSpecification)
                GeometryStyle = styleSpecification;

            if (null != geometry)
                Geometry = geometry;
        }

        public static void GetStyledGeomsAsXML(System.Collections.Generic.ICollection<StyledGeometry> geoms, XmlWriter xmlWriter)
        {
            xmlWriter.WriteStartDocument();
            xmlWriter.WriteStartElement("StyledGeometrys");
            foreach (var item in geoms)
            {
                xmlWriter.WriteStartElement("StyledGeometry");
                xmlWriter.WriteRaw(item.GeometryStyle.ToXML());
                xmlWriter.WriteStartElement("Geometry");
                xmlWriter.WriteString(item.Geometry.AsText());
                xmlWriter.WriteEndElement(); // end geometry
                xmlWriter.WriteEndElement(); // end StyledGeometry
            }
            xmlWriter.WriteEndElement();
            xmlWriter.WriteEndDocument();
        }

        public void RenderNote(EnhancedMapLayer drawLayer)
        {
            SelectGeometry selectGeometry = Geometry.ToSelectGeometry(GeometryStyle, parentMap, drawLayer);
            selectGeometry.Selected += selectGeometry_SelectedForEditing;
        }

        public LocationRect RenderGeometries(Map parentMap, EnhancedMapLayer drawLayer, string geometriesAsXML, RenderState renderState, string itemId = null)
        {
            if (string.IsNullOrEmpty(geometriesAsXML))
                return null;

            LocationRect locationRect = new LocationRect { West = minWest, North = maxNorth, East = maxEast, South = minSouth };
            if (string.IsNullOrEmpty(geometriesAsXML))
                throw new Exception("Empty geometry string");

            ICollection<StyledGeometry> geoms = GetGeoms(geometriesAsXML);

            if (null == drawLayer)
            {
                drawLayer = new EnhancedMapLayer(parentMap);
                if (!parentMap.Children.Contains(drawLayer))
                    parentMap.Children.Add(drawLayer);
            }

            foreach (var item in geoms)
            {
                SelectGeometry selectGeometry = item.Geometry.ToSelectGeometry(item.GeometryStyle, parentMap, drawLayer);
                selectGeometry.ItemID = itemId;

                switch (renderState)
                {
                    case RenderState.Editing:
                        selectGeometry.Selected += selectGeometry_SelectedForEditing;
                        SetLocationRect(ref locationRect, selectGeometry.Geometry);
                        break;
                    case RenderState.ViewingNoInfoBox:
                        selectGeometry.EnableSelection = false;
                        SetLocationRect(ref locationRect, selectGeometry.Geometry);
                        break;
                    case RenderState.ViewingWithInfoBox:
                        selectGeometry.Selected += selectGeom_Selected;
                        break;
                    default:
                        break;
                }
            }
            return locationRect;
        }

        private void SetLocationRect(ref LocationRect locationRect, IGeometry geom)
        {

            if (geom.GeometryType.Equals("Point") && locationRect.HasInitValues())
            {
                locationRect.West = geom.Coordinate.X - 0.01;
                locationRect.East = geom.Coordinate.X + 0.01;
                locationRect.North = geom.Coordinate.Y + 0.01;
                locationRect.South = geom.Coordinate.Y - 0.01;
                return;
            }

            if (geom.EnvelopeInternal.MaxY > locationRect.North || locationRect.North == maxNorth)
                locationRect.North = geom.EnvelopeInternal.MaxY;
            
            if (geom.EnvelopeInternal.MaxX > locationRect.East || locationRect.East == maxEast)
                locationRect.East = geom.EnvelopeInternal.MaxX;

            if (geom.EnvelopeInternal.MinX < locationRect.West || locationRect.West == minWest)
                locationRect.West = geom.EnvelopeInternal.MinX;

            if (geom.EnvelopeInternal.MinY < locationRect.South || locationRect.South == minSouth)
                locationRect.South = geom.EnvelopeInternal.MinY;
        }

        void selectGeometry_SelectedForEditing(object sender, EventArgs args)
        {
            if (SelectedSelectGeometry != null)
            {
                SelectedSelectGeometry(sender, args);
            }
        }

        void selectGeom_Selected(object sender, EventArgs args)
        {
            SelectGeometry selectGeom = (SelectGeometry)sender;

            InfoTool.InfoToolParams i = InfoTool.InfoToolParams.fromXML(selectGeom.ItemID);
            SMIL.InfoTool.InfoTool it = SMIL.DE.Extensions.Extensions.GetChildObject<SMIL.InfoTool.InfoTool>(selectGeom.MapLayer.ParentMap);
            if (it != null)
            {
                selectGeom.MapLayer.ParentMap.Children.Remove(it);
            }

            it = new InfoTool.InfoTool(i);

            DeepEarth.BingMapsToolkit.Client.Controls.FloatingPanel fp = SMIL.DE.Extensions.Extensions.GetChildObject<DeepEarth.BingMapsToolkit.Client.Controls.FloatingPanel>(selectGeom.MapLayer.ParentMap.Parent, "InfoPanel");

            fp.Content = it;
            fp.Visibility = Visibility.Visible;
            fp.Open();

            it.Visibility = Visibility.Visible;
            it.GetDetails();
        }


        /// <summary>
        /// 
        /// </summary>
        /// <param name="geometriesAsXML"></param>
        /// <returns>ObservableCollection of StyledGeometry> or null if input parameter is null or empty</returns>
        private ICollection<StyledGeometry> GetGeoms(string geometriesAsXML)
        {
            if (string.IsNullOrEmpty(geometriesAsXML))
                throw new Exception("Empty geometry string");

            XDocument xDoc = XDocument.Parse(geometriesAsXML);
            ObservableCollection<StyledGeometry> styledGeometries = new ObservableCollection<StyledGeometry>();

            var xmlStyledGeoms = from x in xDoc.Descendants("StyledGeometry")
                              select x;


            foreach (var item in xmlStyledGeoms)
            {
                var style = from x in item.Descendants("Style")
                            select x;

                var geom = from y in item.Descendants("Geometry")
                           select y;

                StyledGeometry styledGeom = new StyledGeometry();
                styledGeom.GeometryStyle = DeepEarth.BingMapsToolkit.Client.Common.Utilities.ProcessStyleXML(style.First().ToString())["default"];
                styledGeom.Geometry = new GisSharpBlog.NetTopologySuite.IO.WKTReader().Read(geom.First().Value);

                styledGeometries.Add(styledGeom);
            }

            return styledGeometries;

        }
    }

    public static class LocationExtensions
    {
        public static bool HasInitValues(this LocationRect locationRect)
        {
            return  (locationRect.South == -90 && locationRect.North == 90 && locationRect.East == 180 && locationRect.West == -180) ? true : false;
        }
    }
}
